/*
 Copyright (c) 2013 MeherTJ G. All rights reserved.
 License: LGPL for personnal study or free software.
 */

#include "zen_image_coder.h"
#include "zen_file.h"
#include "zen_log.h"
#include "zen_endian.h"
#include "zen_exception.h"

#define STB_IMAGE_WRITE_IMPLEMENTATION

#include "stb_image_write.h"

#define STB_IMAGE_IMPLEMENTATION

#include "stb_image.h"

namespace Zen
{
	struct ImageRHead
	{
		char sign[4] = "img";
		uint16_t width  = 0;
		uint16_t height = 0;
		
		void set(uint16_t width_, uint16_t height_, ePixel fmt)
		{
			width = HostNet16(width);
			height = HostNet16(height);
			sign[3] = (int)fmt;
		}
		ePixel format()
		{
			return (ePixel)sign[3];
		}
	};
	
	std::shared_ptr<Image> ImageRDecoder::decode(std::vector<uint8_t> const & data)
	{
		ImageRHead head;
		musts(data.size() >= sizeof(head), "invalid image raw");
		
		::memcpy(&head, data.data(), sizeof(head));
		
		uint32_t width = HostNet32(head.width);
		uint32_t height = HostNet32(head.height);
		
		uint32_t bpp = (int)head.format();;
		
		musts(bpp > 0 && bpp <= 4, "invalid format");
		
		uint32_t sz = width * height * bpp;
		
		musts(data.size() >= sz + sizeof(head), "too few data length");
		
		auto image = Image::Create(head.format(), width, height);
		must(image->size() == sz);
		
		::memcpy(image->data(), data.data() + sizeof(head), sz);
		return image;
	}

	std::vector<uint8_t> ImageREncoder::encode(Image const & image)
	{
		ImageRHead head;
		
		head.set((uint32_t)image.width(), (uint32_t)image.height(), image.format());
		
		auto head_buf = reinterpret_cast<char const *>(&head);
		
		std::vector<uint8_t> data;
		data.reserve(sizeof(head) + image.size());
		data.assign(head_buf, head_buf + sizeof(head));
		data.insert(data.end(), image.data(), image.data() + image.size());
		
		return data;
	}
	
	static void WriteFunc(void *context, void *data, int size) {
		auto & v = *(std::vector<uint8_t>*)context;
		v.insert(v.end(), (uint8_t*)data, (uint8_t*)data + size);
	}

	std::vector<uint8_t> ImageJPGEncoder::encode(Image const & image)
	{
		std::vector<uint8_t> v;
		int w = (int)image.width();
		int h = (int)image.height();
		int comp = (int)image.format();
		int q = (int)(quality*100.f);
		if(stbi_write_jpg_to_func(&WriteFunc, &v, w, h, comp, image.data(), q)) return v;
		return {};
	}
	std::vector<uint8_t> ImagePNGEncoder::encode(Image const & image)
	{
		std::vector<uint8_t> v;
		int w = (int)image.width();
		int h = (int)image.height();
		int comp = (int)image.format();
		if(stbi_write_png_to_func(&WriteFunc, &v, w, h, comp, image.data(), 0)) return v;
		return {};
	}
	std::shared_ptr<Image> ImageDefaultDecoder::decode(const std::vector<uint8_t> &data)
	{
		int x = 0, y = 0, comp = 0;
		auto p = stbi_load_from_memory(data.data(), (int)data.size(), &x, &y, &comp, 0);

		if(!p)
		{
			Zen::LogD("load image failed");
			return nullptr;
		}
		std::shared_ptr<stbi_uc> r(p, [](stbi_uc*p) { free(p); });

		if(comp > 0 && comp <= 4)
		{
			 return Image::CreateWithData((ePixel)comp, (size_t)x, (size_t)y, r.get());
		}
		return nullptr;
	}
	std::vector<uint8_t> ImageTGAEncoder::encode(Image const & image)
	{
		std::vector<uint8_t> v;
		int w = (int)image.width();
		int h = (int)image.height();
		int comp = (int)image.format();
		if(stbi_write_tga_to_func(&WriteFunc, &v, w, h, comp, image.data())) return v;
		return {};
	}
	std::vector<uint8_t> ImageBMPEncoder::encode(Image const & image)
	{
		std::vector<uint8_t> v;
		int w = (int)image.width();
		int h = (int)image.height();
		int comp = (int)image.format();
		if(stbi_write_bmp_to_func(&WriteFunc, &v, w, h, comp, image.data())) return v;
		return {};
	}
}

